//////////////////////////////////////////////////////////////////////
// BoundingBox.h
//
// SHEN Fangyang
// me@shenfy.com
//
// Copyright (C) SHEN Fangyang, 2011, All rights reserved.
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
//  Brief Description
//
//	3D Bounding Box
//
//////////////////////////////////////////////////////////////////////
#pragma once

#include <SysTypeTraits.h>
#include <LA/Vec3.h>


namespace grflib
{
    namespace engine
    {
        namespace core
        {
            //T must be a system type
            template<typename T, UINT Dim = 3>
            struct BBox
            {
                BBox(void); //infinite big box by default
                BBox(T length); //cube centered at (0,0,0,...) with side length = length
                BBox(T *vMin, T *vMax);
                BBox(const BBox<T, Dim> &box);
                ~BBox(void) {}

                BBox<T, Dim> &operator= (const BBox<T, Dim> &box);

                void Cube(T length);
                void Clear(void);
                void SetValue(T *vMin, T *vMax);
                void AddMin(UINT dim, T value);
                void AddMax(UINT dim, T value);
                void AddMinMax(UINT dim, T value);

                //shift
                BBox<T, Dim> &operator+= (T *shift);

                //merge with another box
                void Merge(const BBox<T, Dim> &box);
                friend BBox<T, Dim> Merge(const BBox<T, Dim> &box1, const BBox<T, Dim> &box2);

                //data members
                T m_min[Dim];
                T m_max[Dim];
                T m_center[Dim];
                T m_halfsize[Dim];
            };

            template<typename T, UINT Dim>
            BBox<T, Dim>::BBox()
            {
                T minvalue = w32lib::SysTypeTraits<T>::MinVal();
                T maxvalue = w32lib::SysTypeTraits<T>::MaxVal();

                for (UINT i = 0; i < Dim; i++)
                {
                    m_min[i] = maxvalue;
                    m_max[i] = minvalue;
                    m_center[i] = (maxvalue + minvalue)/2.0f;
                    m_halfsize[i] = (maxvalue - minvalue)/2.0f;
                }
            }

            template<typename T, UINT Dim>
            BBox<T, Dim>::BBox(const BBox<T, Dim> &box)
            {
                memcpy(m_min, box.m_min, sizeof(T) * Dim);
                memcpy(m_max, box.m_max, sizeof(T) * Dim);
                memcpy(m_center, box.m_center, sizeof(T) * Dim);
                memcpy(m_halfsize, box.m_halfsize, sizeof(T) * Dim);
            }

            template<typename T, UINT Dim>
            BBox<T, Dim> &BBox<T, Dim>::operator= (const BBox<T, Dim> &box)
            {
                if (this != &box)
                {
                    memcpy(m_min, box.m_min, sizeof(T) * Dim);
                    memcpy(m_max, box.m_max, sizeof(T) * Dim);
                    memcpy(m_center, box.m_center, sizeof(T) * Dim);
                    memcpy(m_halfsize, box.m_halfsize, sizeof(T) * Dim);
                }
                return *this;
            }


            template<typename T, UINT Dim>
            void BBox<T, Dim>::Clear()
            {
                T minvalue = w32lib::SysTypeTraits<T>::MinVal();
                T maxvalue = w32lib::SysTypeTraits<T>::MaxVal();

                for (UINT i = 0; i < Dim; i++)
                {
                    m_min[i] = maxvalue;
                    m_max[i] = minvalue;
                    m_center[i] = (maxvalue + minvalue)/2.0f;
                    m_halfsize[i] = (maxvalue - minvalue)/2.0f;
                }
            }

            template<typename T, UINT Dim>
            BBox<T, Dim>::BBox(T length)
            {
                for (UINT i = 0; i < Dim; i++)
                {
                    //place the center at the origin
                    m_min[i] = - length / 2;
                    m_max[i] = length / 2;
                    m_center[i] = 0.0f;
                    m_halfsize[i] = length / 2;
                }			
            }

            template<typename T, UINT Dim>
            void BBox<T, Dim>::Cube(T length)
            {
                for (UINT i = 0; i < Dim; i++)
                {
                    //place the center at the origin
                    m_min[i] = - length / 2;
                    m_max[i] = length / 2;
                    m_center[i] = 0.0f;
                    m_halfsize[i] = length / 2;
                }			
            }

            template<typename T, UINT Dim>
            BBox<T, Dim>::BBox(T *vMin, T *vMax)
            {
                memcpy(m_min, vMin, sizeof(T) * Dim);
                memcpy(m_max, vMax, sizeof(T) * Dim);

                for (UINT i = 0; i < Dim; i++)
                {
                    m_center[i] = (m_max[i] + m_min[i])/2.0f;
                    m_halfsize[i] = (m_max[i] - m_min[i])/2.0f;
                }
            }

            template<typename T, UINT Dim>
            void BBox<T, Dim>::SetValue(T *vMin, T *vMax)
            {
                memcpy(m_min, vMin, sizeof(T) * Dim);
                memcpy(m_max, vMax, sizeof(T) * Dim);
                for (UINT i = 0; i < Dim; i++)
                {
                    m_center[i] = (m_max[i] + m_min[i])/2.0f;
                    m_halfsize[i] = (m_max[i] - m_min[i])/2.0f;
                }
            }

            template<typename T, UINT Dim>
            void BBox<T, Dim>::AddMin(UINT dim, T value)
            {
                if (m_min[dim] > value)
                {
                    m_min[dim] = value;
                    m_center[dim] = (m_max[dim] + m_min[dim])/2.0f;
                    m_halfsize[dim] = (m_max[dim] - m_min[dim])/2.0f;
                }
            }

            template<typename T, UINT Dim>
            void BBox<T, Dim>::AddMax(UINT dim, T value)
            {
                if (m_max[dim] < value)
                {
                    m_max[dim] = value;
                    m_center[dim] = (m_max[dim] + m_min[dim])/2.0f;
                    m_halfsize[dim] = (m_max[dim] - m_min[dim])/2.0f;
                }
            }

            template<typename T, UINT Dim>
            void BBox<T, Dim>::AddMinMax(UINT dim, T value)
            {
                bool update = false;
                if (m_max[dim] < value)
                {
                    m_max[dim] = value;
                    update = true;
                }
                if (m_min[dim] > value)
                {
                    m_min[dim] = value;
                    update = true;
                }
                if ( update )
                {
                    m_center[dim] = (m_max[dim] + m_min[dim])/2.0f;
                    m_halfsize[dim] = (m_max[dim] - m_min[dim])/2.0f;
                }
            }

            template<typename T, UINT Dim>
            void BBox<T, Dim>::Merge(const BBox<T, Dim> &box)
            {
                for (UINT i = 0; i < Dim; i++)
                {
                    bool update = false;
                    if (m_min[i] > box.m_min[i])
                    {
                        m_min[i] = box.m_min[i];
                        update = true;
                    }
                    if (m_max[i] < box.m_max[i])
                    {
                        m_max[i] = box.m_max[i];
                        update = true;
                    }
                    if ( update )
                    {
                        m_center[i] = (m_max[i] + m_min[i])/2.0f;
                        m_halfsize[i] = (m_max[i] - m_min[i])/2.0f;
                    }
                }
            }

            template<typename T, UINT Dim>
            BBox<T, Dim> Merge(const BBox<T, Dim> &box1, const BBox<T, Dim> &box2)
            {
                BBox<T, Dim> result(box1);

                for (UINT i = 0; i < Dim; i++)
                {
                    if (result.m_min[i] > box2.m_min[i])
                        result.m_min[i] = box2.m_min[i];
                    if (result.m_max[i] < box2.m_max[i])
                        result.m_max[i] = box2.m_max[i];

                    result.m_center[i] = (result.m_max[i] + result.m_min[i]) / 2.0f;
                    result.m_halfsize[i] = (result.m_max[i] - result.m_min[i]) / 2.0f;
                }
                return result;
            }

            template<typename T, UINT Dim>
            BBox<T, Dim> & BBox<T, Dim>::operator+= (T *shift)
            {
                for (UINT i = 0; i < Dim; i++)
                {
                    m_min[i] += shift[i];
                    m_max[i] += shift[i];
                    m_center[i] += shift[i];
                }
                return (*this);
            }
        } // namespace core
    } //namespace engine
} //namespace grflib